package com.qlogic.commons.platform.impls.dal.toplink.persistence;

import java.util.Collection;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Vector;

import oracle.toplink.exceptions.DatabaseException;
import oracle.toplink.exceptions.OptimisticLockException;
import oracle.toplink.queryframework.ObjectLevelReadQuery;
import oracle.toplink.threetier.ClientSession;
import oracle.toplink.sessions.UnitOfWork;

import com.qlogic.commons.exceptions.BaseException;
import com.qlogic.commons.platform.AbstractService;
import com.qlogic.commons.platform.ServiceActivationException;
import com.qlogic.commons.platform.ServiceDesactivationException;
import com.qlogic.commons.platform.ServiceExecutionException;
import com.qlogic.commons.platform.ServiceInitializationException;
import com.qlogic.commons.platform.ServiceStartingException;
import com.qlogic.commons.platform.ServiceStopingException;
import com.qlogic.commons.platform.impls.dal.toplink.ToplinkServer;
import com.qlogic.commons.resources.Resource;

public final class ToplinkPersistenceService extends AbstractService {

	/**
	 * 
	 */
	private static final long serialVersionUID = 4939380603513995635L;

	public long NULL_TRANSACTION_ID = Long.MIN_VALUE;

	public static final short COMMIT_AND_RESUME_ON_FAILURE = 0;

	public static final short COMMIT_AND_RESUME = 1;

	public static final short READ_WAIT_LOCK = 0;

	public static final short READ_NO_WAIT_LOCK = 1;

	public static final short READ_NO_LOCK = 2;

	private long trxCnt = -1;

	private StringBuffer topLinkCodeTrad = new StringBuffer();

	private boolean debug = false;
	
	private ClientSession clientSession;
	
	protected Resource serviceResource;
	
	private Hashtable trxs = new Hashtable ();

	private Hashtable trxObjects;

	public void initialize () throws ServiceInitializationException {
		try {
			serviceResource = configuration.getResource ("../../server");
		} catch (BaseException e) {
			throw new ServiceInitializationException (e);
		} 
	}

	public ToplinkPersistenceService () throws ServiceInitializationException {
	}
	
	/**
	 * @see com.qlogic.commons.platform.Service#activate()
	 */
	public void activate() throws ServiceActivationException {
		activated = true;
	}

	/**
	 * @see com.qlogic.commons.platform.Service#start()
	 */
	public void start() throws ServiceStartingException {
		started = true;
	}

	/**
	 * @see com.qlogic.commons.platform.Service#desactivate()
	 */
	public void desactivate() throws ServiceDesactivationException {
		activated = false;
	}

	/**
	 * @see com.qlogic.commons.platform.Service#stop()
	 */
	public void stop() throws ServiceStopingException {
		started = false;
	}

	public void newSession () throws ServiceExecutionException {
		ToplinkServer toplinkServer;
		try {
			toplinkServer = (ToplinkServer)serviceResource.getValue ();
		} catch (BaseException e) {
			throw new ServiceExecutionException (e);
		} 
		clientSession = toplinkServer.newClientSession ();
	}

	public void useSession (Object session) throws ServiceExecutionException {
		if (session == null)
			throw new ServiceExecutionException (
					"Can not create Instance of ToplinkPersistenceService with a null session, use newSession instead !");
		clientSession = (ClientSession)session;
		trxObjects = new Hashtable ();
		trxs = new Hashtable();
		writeProviderCode("session = (ClientSession)_session;");
		console("New Instance (" + this.hashCode()
				+ ") given by the session hashCode (" + session.hashCode()
				+ ")");
	}

	public long newTrx(Object trx) throws ServiceExecutionException {
		if (trx == null)
			throw new ServiceExecutionException(
					"Enable to create a NULL transaction from the current Session ["
							+ clientSession.hashCode()
							+ "] -> newTrx(Not Null Object)");
		Long id = new Long(++trxCnt);
		if (trxs == null)
			trxs = new Hashtable ();
		trxs.put(id, (UnitOfWork) trx);
		if (trxObjects == null)
			trxObjects = new Hashtable ();
		trxObjects.put (id, new TrxObjects(trxCnt));
		writeProviderCode("trxs.put(new Long(" + trxCnt
				+ "), (UnitOfWork)trx);");
		console("New Transaction created " + getTrxInfo(trxCnt, trx));
		return trxCnt;
	}

	public long newTrx() throws ServiceExecutionException {
		return newTrx(false);
	}

	public long newTrx(boolean beginNow) throws ServiceExecutionException {
		try {
			initialize();
		} catch (ServiceInitializationException e) {
			throw new ServiceExecutionException (e.getCause ());
		}
		UnitOfWork uow = null;
		try {
			uow = clientSession.acquireUnitOfWork ();
			Long id = new Long (++trxCnt);
			if (trxs == null)
				trxs = new Hashtable ();
			trxs.put(id, uow);
			if (trxObjects == null)
				trxObjects = new Hashtable ();
			trxObjects.put(id, new TrxObjects(trxCnt));
			writeProviderCode("UnitOfWork uow = session.acquireUnitOfWork();");
			writeProviderCode("trxs.put(new Long(" + trxCnt + "), uow);");
			console("New Transaction created " + getTrxInfo(trxCnt, uow));
			if (beginNow) {
				uow.beginEarlyTransaction();
				console("Begin Early Transaction " + getTrxInfo(trxCnt, uow));
			}
		} catch (Exception e) {
			throw new ServiceExecutionException(
					"Enable to start transaction from the current Session ["
							+ clientSession.hashCode() + "]", e);
		}
		return trxCnt;
	}

	public Object getTrx(long id) throws ServiceExecutionException {
		if (id == NULL_TRANSACTION_ID) {
			id = trxCnt;
		}
		writeProviderCode("trx" + id + " = trxs.get(new Long(" + id + "));");
		Object trx = trxs.get (new Long(id));
		console("Getting Transation [" + id + "] - " + getTrxInfo(id, trx));
		return trx;
	}

	private long getTrxId(long id) {
		if (id == NULL_TRANSACTION_ID) {
			return trxCnt;
		}
		return id;
	}

	public long getResponsableTrx(Object object) {
		Enumeration enumKeys = trxObjects.keys();
		while (enumKeys.hasMoreElements()) {
			Long key = (Long) enumKeys.nextElement();
			TrxObjects trxObjectsItem = (TrxObjects) trxObjects.get(key);
			if (trxObjectsItem.containsObject(object.hashCode()))
				return trxObjectsItem.getTrxId();
		}
		return NULL_TRANSACTION_ID;
	}

	public void commitTrx(long id) throws ServiceExecutionException {
		Object trx = getTrx(id);
		id = getTrxId (id);
		if (trx == null)
			throw new ServiceExecutionException ("No Transaction Found [" + id
					+ "] to be commited");
		console("Start commiting Transaction ...");
		try {
			((UnitOfWork) trx).commit();
		} catch (DatabaseException dbEx) {
			throw new ServiceExecutionException (
					String.valueOf (dbEx.getDatabaseErrorCode()),
									"Transaction ["	+ id + 
									"] can not be commited due to ServerSide Fired Exception !!!",
									dbEx);
		} catch (OptimisticLockException olEx) {
			throw new ServiceExecutionException (
					String.valueOf (olEx.getErrorCode()),
									"Transaction ["	+ id + 
									"] can not be commited due to ServerSide Fired Exception !!!",
									olEx);
		}
		writeProviderCode("((UnitOfWork)" + "trx" + id + ").commit();");
		console("Transaction commited - " + getTrxInfo(id, trx));
	}
	
	public void commitTrx() throws ServiceExecutionException {
		commitTrx(NULL_TRANSACTION_ID);
	}

	public void commitTrx(long id, short commitMode) throws ServiceExecutionException {
		Object trx = getTrx(id);
		id = getTrxId (id);
		if (trx == null)
			throw new ServiceExecutionException ("No Transaction Found [" + id
					+ "] to be commited");
		try {
			console("Start commiting Transaction ...");
			if (commitMode == COMMIT_AND_RESUME)
				((UnitOfWork) trx).commitAndResume();
			else if (commitMode == COMMIT_AND_RESUME_ON_FAILURE)
				((UnitOfWork) trx).commitAndResumeOnFailure();
			else
				((UnitOfWork) trx).commit();
		} catch (DatabaseException dbEx) {
			throw new ServiceExecutionException(
					String.valueOf (dbEx.getDatabaseErrorCode()),
									"Transaction ["	+ id + "], with the given CommitMode ["	+ 
									commitMode + 
									"] can not be commited due to ServerSide Fired Exception !!!",
									dbEx);
		} catch (OptimisticLockException olEx) {
			throw new ServiceExecutionException(
					String.valueOf (olEx.getErrorCode()),
									"Transaction [" + id + 
									"], with the given CommitMode [" + commitMode + 
									"] can not be commited due to ServerSide Fired Exception !!!",
									olEx);
		}
		writeProviderCode("((UnitOfWork)" + "trx" + id + ").commit();");
		console("Transaction commited " + getTrxInfo(id, trx));
	}

	public void commitTrx (short commitMode) throws ServiceExecutionException {
		commitTrx (NULL_TRANSACTION_ID, commitMode);
	}

	public void rollbackTrx(long id) throws ServiceExecutionException {
		Object trx = getTrx(id);
		id = getTrxId (id);
		if (trx == null)
			throw new ServiceExecutionException("No Transaction Found [" + id
					+ "] to be rooled back");
		try {
			((UnitOfWork) trx).revertAndResume();
		} catch (Exception e) {
			throw new ServiceExecutionException(
					"Transaction ["
							+ id
							+ "] can not be rooled back due to Persistence Manager Fired Exception when applying revertAndResume !!!",
					e);
		}
		console("Transaction rolled back " + getTrxInfo(id, trx));
	}

	public void rollbackTrx () throws ServiceExecutionException {
		rollbackTrx (NULL_TRANSACTION_ID);
	}

	public Object makePersistent(long id, Object object) throws ServiceExecutionException {
		if (object == null) {
			console("makePersistent will ignore making Object in persistent state du to the Nullability of this one");
			return null;
		}
		Object trx = getTrx(id);
		id = getTrxId (id);
		if (trx == null)
			throw new ServiceExecutionException("No Transaction Found [" + id
					+ "] to process object persistence");
		UnitOfWork uow = ((UnitOfWork) trx);
		console("Start persisting object (" + this.hashCode() + "."
				+ clientSession.hashCode() + "." + uow.hashCode() + ":" + id
				+ ") - Object (" + object.hashCode() + ")");
		Object retObj = null;
		try {
			retObj = uow.registerObject(object);
		} catch (Exception e) {
			throw new ServiceExecutionException(
					"Transaction ["
							+ id
							+ "] can not register object due to Persistence Manager Fired Exception when applying registerObject !!!",
					e);
		}
		TrxObjects trxObjectsItem = (TrxObjects) trxObjects.get(new Long(id));
		trxObjectsItem.registerObject(retObj);
		console("makePersistent Object - After Persistence "
				+ getTrxInfo(id, uow) + " - Object|old:new| ("
				+ object.hashCode() + ":" + retObj.toString() + ")");
		writeProviderCode("return trx" + id + ".registerObject("
				+ object.hashCode() + ");");
		return retObj;
	}

	public Object makePersistent(Object object) throws ServiceExecutionException {
		return makePersistent(NULL_TRANSACTION_ID, object);
	}
	
	public Object makePersistent(long id, Object object, boolean force)
			throws ServiceExecutionException {
		if (force) {
			unregisterPersistentObject(id, object);
		}
		return makePersistent(id, object);
	}

	public Object makePersistent(Object object, boolean force) throws ServiceExecutionException {
		return makePersistent(NULL_TRANSACTION_ID, object, force);
	}
	
	public Collection makePersistentAll(long id, Collection objects)
			throws ServiceExecutionException {
		if (objects == null || objects.isEmpty()) {
			console("makePersistent Collection (NULL OR EMPTY)");
			return null;
		}
		Object trx = getTrx(id);
		id = getTrxId (id);
		if (trx == null)
			throw new ServiceExecutionException("No Transaction Found [" + id
					+ "] to process object persistence");
		UnitOfWork uow = ((UnitOfWork) trx);
		console("makePersistent Collection - Before Persistence "
				+ getTrxInfo(id, uow) + " - Collection (" + objects.hashCode()
				+ ")");
		Collection retObj = null;
		try {
			retObj = uow.registerAllObjects((Vector) objects);
		} catch (Exception e) {
			throw new ServiceExecutionException(
					"Transaction ["
							+ id
							+ "] can not register collection due to Persistence Manager Fired Exception when applying registerAllObjects !!!",
					e);
		}
		TrxObjects trxObjectsItem = (TrxObjects) trxObjects.get(new Long(id));
		trxObjectsItem.registerAllObjects(retObj);
		console("makePersistent Collection - After Persistence "
				+ getTrxInfo(id, uow) + " - Collection (" + retObj.hashCode()
				+ ")");
		writeProviderCode("return trx" + id + ".registerAllObjects("
				+ objects.hashCode() + ");");
		return retObj;
	}

	public Collection makePersistentAll(Collection objects)
		throws ServiceExecutionException {
		return makePersistentAll(NULL_TRANSACTION_ID, objects);
	}
	
	public void delete(long id, Object object) throws ServiceExecutionException {
		Object trx = getTrx(id);
		id = getTrxId (id);
		if (trx == null)
			throw new ServiceExecutionException("No Transaction Found [" + id
					+ "] to process object deletion");
		UnitOfWork uow = ((UnitOfWork) trx);
		if (object != null) {
			try {
				uow.deleteObject(object);
			} catch (Exception e) {
				throw new ServiceExecutionException(
						"Transaction ["
								+ id
								+ "] can not delete object due to Persistence Manager Fired Exception when applying deleteObject !!!",
						e);
			}
			console("delete Object " + getTrxInfo(id, uow) + " - Object ("
					+ object.hashCode() + ")");
			writeProviderCode("if(" + object.hashCode() + " != null){");
			writeProviderCode("\ttrx" + id + ".deleteObject("
					+ object.hashCode() + ");");
			writeProviderCode("}");
		} else {
			writeProviderCode("if(null != null){");
			writeProviderCode("\ttrx" + id + ".deleteObject(null);");
			writeProviderCode("}");
		}
	}

	public void delete (Object object) throws ServiceExecutionException {
		delete(NULL_TRANSACTION_ID, object);
	}
	
	public void deleteAll (long id, Collection objects) throws ServiceExecutionException {
		Object trx = getTrx(id);
		id = getTrxId (id);
		if (trx == null)
			throw new ServiceExecutionException ("No Transaction Found [" + id
					+ "] to process collection deletion");
		UnitOfWork uow = ((UnitOfWork) trx);
		if (objects != null && !objects.isEmpty()) {
			try {
				uow.deleteAllObjects(objects);
			} catch (Exception e) {
				throw new ServiceExecutionException(
						"Transaction ["
								+ id
								+ "] can not delete collection due to Persistence Manager Fired Exception when applying deleteAllObjects !!!",
						e);
			}
			console("delete Collection " + getTrxInfo(id, uow)
					+ " - Collection (" + objects.hashCode() + ")");
			writeProviderCode("if(" + objects.hashCode() + "[" + objects.size()
					+ "] != null  && !" + objects.hashCode() + ".isEmpty()){");
			writeProviderCode("\ttrx" + id + ".deleteAllObjects("
					+ objects.hashCode() + ");");
			writeProviderCode("}");
		} else {
			writeProviderCode("if(null != null){");
			writeProviderCode("\ttrx" + id + ".deleteAllObjects(null);");
			writeProviderCode("}");
		}
	}

	public void deleteAll (Collection objects) throws ServiceExecutionException {
		deleteAll (NULL_TRANSACTION_ID, objects);
	}
	
	public Object lookup(long id, Object object) throws ServiceExecutionException {
		return lookup(id, object, false);
	}

	public Object lookup(Object object) throws ServiceExecutionException {
		return lookup(NULL_TRANSACTION_ID, object);
	}

	public Object lookup(Object object, boolean force) throws ServiceExecutionException {
		return lookup(NULL_TRANSACTION_ID, object, force);
	}

	public Object lookup(long id, Object object, boolean force)
			throws ServiceExecutionException {
		if (force) {
			return lookup(id, object, READ_NO_LOCK);
		}
		Object trx = getTrx(id);
		id = getTrxId (id);
		if (trx == null) {
			throw new ServiceExecutionException("No Transaction Found [" + id
					+ "] to process object lookup");
		}
		UnitOfWork uow = ((UnitOfWork) trx);
		if (object == null) {
			console("Lookup Object (" + this.hashCode() + "."
					+ clientSession.hashCode() + "." + uow.hashCode() + ":" + id
					+ ") - Object (NULL)");
			return null;
		}
		Object retObj = null;
		try {
			retObj = uow.readObject(object);
		} catch (Exception e) {
			throw new ServiceExecutionException(
					"Transaction ["
							+ id
							+ "] can not read object due to Persistence Manager Fired Exception when applying readObject !!!",
					e);
		}
		TrxObjects trxObjectsItem = (TrxObjects) trxObjects.get(new Long(id));
		trxObjectsItem.registerObject(retObj);
		writeProviderCode("Object retObj = trx" + id + ".readObject("
				+ object.hashCode() + ");");
		if (retObj != null)
			console("lookup Object " + getTrxInfo(id, uow) + " - Object ("
					+ retObj.hashCode() + ":" + retObj.toString() + ")");
		else
			console("lookup Object " + getTrxInfo(id, uow) + " - Object ("
					+ object.hashCode() + " -> NULL)");
		writeProviderCode("if(retObj != null){");
		writeProviderCode("\treturn retObj");
		writeProviderCode("}");
		return retObj;
	}

	public Object lookup(long id, Object object, short lockMode)
			throws ServiceExecutionException {
		Object trx = getTrx(id);
		id = getTrxId (id);
		if (trx == null) {
			throw new ServiceExecutionException("No Transaction Found [" + id
					+ "] to process object lookup");
		}
		UnitOfWork uow = ((UnitOfWork) trx);
		if (object == null) {
			console("Lookup Object (" + this.hashCode() + "."
					+ clientSession.hashCode() + "." + uow.hashCode() + ":" + id
					+ ") - Object (NULL)");
			return null;
		}
		Object retObj = null;
		try {
			if (lockMode == READ_WAIT_LOCK)
				retObj = uow.refreshAndLockObject(object,
						ObjectLevelReadQuery.LOCK);
			else if (lockMode == READ_NO_WAIT_LOCK)
				retObj = uow.refreshAndLockObject(object,
						ObjectLevelReadQuery.LOCK_NOWAIT);
			else
				retObj = uow.refreshAndLockObject(object,
						ObjectLevelReadQuery.NO_LOCK);
		} catch (Exception e) {
			throw new ServiceExecutionException(
					"Transaction ["
							+ id
							+ "] can not read object with the given lookup mode ["
							+ lockMode
							+ "] due to Persistence Manager Fired Exception when applying refreshAndLockObject !!!",
					e);
		}
		TrxObjects trxObjectsItem = (TrxObjects) trxObjects.get(new Long(id));
		trxObjectsItem.registerObject(retObj);
		writeProviderCode("Object retObj = trx" + id + ".readObject("
				+ object.hashCode() + ");");
		if (retObj != null)
			console("lookup Object " + getTrxInfo(id, uow) + " - Object ("
					+ retObj.hashCode() + ":" + retObj.toString() + ")");
		else
			console("lookup Object " + getTrxInfo(id, uow) + " - Object ("
					+ object.hashCode() + " -> NULL)");
		writeProviderCode("if(retObj != null){");
		writeProviderCode("\treturn retObj");
		writeProviderCode("}");
		return retObj;
	}

	public Object lookup (Object object, short lockMode) throws ServiceExecutionException {
		return lookup(NULL_TRANSACTION_ID, object, lockMode);
	}

	public Collection lookupAll(long id, Collection objects)
			throws ServiceExecutionException {
		return lookupAll(id, objects, false);
	}

	public Collection lookupAll (Collection objects) throws ServiceExecutionException {
		return lookupAll(NULL_TRANSACTION_ID, objects);
	}

	public Collection lookupAll (Collection objects, boolean force) throws ServiceExecutionException {
		return lookupAll(NULL_TRANSACTION_ID, objects, force);
	}

	public Collection lookupAll(long id, Collection objects, boolean force)
			throws ServiceExecutionException {
		Vector retObjs = null;
		Object trx = getTrx(id);
		id = getTrxId (id);
		if (trx == null) {
			throw new ServiceExecutionException("No Transaction Found [" + id
					+ "] to process collection lookup");
		}
		UnitOfWork uow = ((UnitOfWork) trx);
		if (objects == null || objects.isEmpty()) {
			console("Lookup Collection (" + this.hashCode() + "."
					+ clientSession.hashCode() + "." + uow.hashCode() + ":" + id
					+ ") - Collection (NULL OR EMPTY)");
			return null;
		} else {
			retObjs = new Vector (objects.size());
		}
		Iterator iterObjects = objects.iterator();
		try {
			while (iterObjects.hasNext()) {
				Object currObject = iterObjects.next();
				if (force)
					retObjs.addElement(uow.refreshAndLockObject(currObject,
							ObjectLevelReadQuery.NO_LOCK));
				else
					retObjs.addElement(uow.readObject(currObject));
			}
		} catch (Exception e) {
			throw new ServiceExecutionException(
					"Transaction ["
							+ id
							+ "] can not read object due to Persistence Manager Fired Exception when applying readObject !!!",
					e);
		}
		TrxObjects trxObjectsItem = (TrxObjects) trxObjects.get(new Long(id));
		trxObjectsItem.registerAllObjects(retObjs);
		writeProviderCode("Collection retObjs = trx" + id + ".readAllObjects("
				+ objects.hashCode() + ");");
		if (retObjs != null)
			console("lookup Collection " + getTrxInfo(id, uow) + " - Object ("
					+ retObjs.hashCode() + ")");
		else
			console("lookup Collection " + getTrxInfo(id, uow) + " - Object ("
					+ objects.hashCode() + " -> NULL)");
		writeProviderCode("if(retObjs != null){");
		writeProviderCode("\treturn retObjs");
		writeProviderCode("}");
		return (Collection) retObjs;
	}

	public boolean unregisterPersistentObject(long id, Object persistentObject)
			throws ServiceExecutionException {
		if (persistentObject == null)
			return false;
		TrxObjects trxObjectsItem = (TrxObjects) trxObjects.get(new Long(id));
		boolean existentObject = trxObjectsItem.containsObject(persistentObject
				.hashCode());
		Object trx = getTrx(id);
		id = getTrxId (id);
		if (trx == null)
			throw new ServiceExecutionException("No Transaction Found [" + id
					+ "] to process unregister persistent object");
		UnitOfWork uow = ((UnitOfWork) trx);
		try {
			uow.unregisterObject(persistentObject);
		} catch (Exception e) {
			throw new ServiceExecutionException(
					"Transaction ["
							+ id
							+ "] can not unregister object ["
							+ persistentObject
							+ "] due to Persistence Manager Fired Exception when applying unregisterObject !!!",
					e);
		}
		console("Unregistring Object " + getTrxInfo(id, uow) + " - Object ("
				+ persistentObject.hashCode() + ")");
		if (existentObject)
			trxObjectsItem.removeObject(persistentObject.hashCode());
		return existentObject;
	}

	/**
	 * Clear all deleted and new objects. Revert should not be confused with
	 * release which it the normal compliment to commit. Revert is more similar
	 * to commit and resume, however reverts all changes and resumes.
	 */
	public void undoTransactionChanges(long id) throws ServiceExecutionException {
		Object trx = getTrx(id);
		id = getTrxId (id);
		if (trx == null) {
			throw new ServiceExecutionException("No Transaction Found [" + id
					+ "] to process unregister all persistent objects");
		}
		UnitOfWork uow = ((UnitOfWork) trx);
		try {
			uow.revertAndResume();
			console("Unregister All Objects : " + getTrxInfo(id, uow));
		} catch (Exception e) {
			throw new ServiceExecutionException(
					"Transaction ["
							+ id
							+ "] can not unregister all objects due to Persistence Manager Fired Exception when applying unregisterObject !!!",
					e);
		}
	}

	public void undoObjectChanges(long id, Object persistentObject)
			throws ServiceExecutionException {
		Object trx = getTrx(id);
		id = getTrxId (id);
		if (trx == null) {
			throw new ServiceExecutionException("No Transaction Found [" + id
					+ "] to undo object's changes");
		}
		UnitOfWork uow = ((UnitOfWork) trx);
		try {
			uow.deepRevertObject(persistentObject);
			console("Undo object's changes : " + getTrxInfo(id, uow));
		} catch (Exception e) {
			throw new ServiceExecutionException(
					"Transaction ["
							+ id
							+ "] can not unregister all objects due to Persistence Manager Fired Exception when applying unregisterObject !!!",
					e);
		}
	}

	public void printRegisteredObjects(long id) throws ServiceExecutionException {
		Object trx = getTrx(id);
		id = getTrxId (id);
		if (trx == null) {
			throw new ServiceExecutionException("No Transaction Found [" + id
					+ "] to process print registered objects");
		}
		UnitOfWork uow = ((UnitOfWork) trx);
		try {
			uow.printRegisteredObjects();
		} catch (Exception e) {
			throw new ServiceExecutionException(
					"Can not print registered objects for transaction ["
							+ id
							+ "] due to Persistence Manager Fired Exception when applying printRegisteredObjects !!!",
					e);
		}
	}

	public void performDeletesFirst(long id, boolean perform)
			throws ServiceExecutionException {
		Object trx = getTrx(id);
		id = getTrxId (id);
		if (trx == null) {
			throw new ServiceExecutionException("No Transaction Found [" + id
					+ "] to process Perform Deletes First");
		}
		UnitOfWork uow = ((UnitOfWork) trx);
		try {
			uow.setShouldPerformDeletesFirst(perform);
		} catch (Exception e) {
			throw new ServiceExecutionException(
					"Can not perform deletes first for transaction ["
							+ id
							+ "] due to Persistence Manager Fired Exception when applying setShouldPerformDeletesFirst !!!",
					e);
		}
	}

	private String getTrxInfo(long trxId, Object trx) {
		return this.hashCode()
				+ "."
				+ ((clientSession == null) ? "[NULL SESSION]" : String
						.valueOf(clientSession.hashCode()))
				+ "."
				+ ((trx == null) ? "[NULL TRANSACTION]" : String.valueOf(trx
						.hashCode())) + ":" + trxId + ")";
	}

	public String getSessionInfo() {
		if (clientSession == null)
			return "Session not acquired Yet";
		else
			return this.hashCode() + "." + clientSession.hashCode();
	}

	public Object getSession()  throws ServiceExecutionException {
		return clientSession;
	}

	public void releaseSession() throws ServiceExecutionException {
		verifySession();
		Enumeration uowEnum = trxs.keys();
		while (uowEnum.hasMoreElements()) {
			releaseTrx(((Long) uowEnum.nextElement()).longValue());
		}
		if (clientSession != null) {
			try {
				ToplinkServer toplinkServer;
				try {
					toplinkServer = (ToplinkServer)serviceResource.getValue ();
				} catch (BaseException e) {
					throw new ServiceExecutionException (e);
				} 

				toplinkServer.releaseClientSession (clientSession);
				console("Session Released (" + clientSession.hashCode() + ")");
				clientSession = null;
				trxObjects = null;
				trxs = null;
				writeProviderCode("session.release();");
			} catch (Exception e) {
				throw new ServiceExecutionException(
						"Can not realease Session due to Persistence Manager Fired Exception when applying release session !!!",
						e);
			}
		}
	}

	public void releaseTrx(long id) throws ServiceExecutionException {
		Object trx = getTrx(id);
		id = getTrxId (id);
		if (trx == null) {
			throw new ServiceExecutionException("No Transaction Found [" + id
					+ "] to be released");
		}
		UnitOfWork uow = ((UnitOfWork) trx);
		writeProviderCode("trx" + id + ".validateObjectSpace();");
		try {
			uow.release();
			writeProviderCode("trx" + id + ".release();");
			console("Release Transaction " + getTrxInfo(id, uow));
			removeTrx(id);
		} catch (Exception e) {
			throw new ServiceExecutionException(
					"Can not realease Transaction ["
							+ id
							+ "] due to Persistence Manager Fired Exception when applying release Transaction !!!",
					e);
		}
	}

	public void markAsReadOnly(long id, Class theClass) throws ServiceExecutionException {
		Object trx = getTrx(id);
		id = getTrxId (id);
		if (trx == null) {
			throw new ServiceExecutionException("No Transaction Found [" + id + "]");
		}
		UnitOfWork uow = ((UnitOfWork) trx);
		try {
			uow.addReadOnlyClass(theClass);
			writeProviderCode("trx" + id + ".addReadOnlyClass("
					+ theClass.getName() + ");");
			console("Class " + theClass + " has been made as read only by ["
					+ getTrxInfo(id, uow) + "]");
		} catch (Exception e) {
			throw new ServiceExecutionException(
					"Can not mark this class as read only using transaction ["
							+ id
							+ "] due to Persistence Manager Fired Exception when applying release Transaction !!!",
					e);
		}
	}

	/**
	 * copier un objet persitant (l'objet resultat n est pas persistant) pour
	 * persister utiliser createObject
	 */
	public Object copyObject(long id, Object object) throws ServiceExecutionException {
		Object trx = getTrx(id);
		id = getTrxId (id);
		if (trx == null) {
			throw new ServiceExecutionException("No Transaction Found [" + id
					+ "] to be released");
		}
		UnitOfWork uow = ((UnitOfWork) trx);
		return uow.copyObject(object);
	}

	/**
	 * return true si la transaction contient ai moins une operation
	 */
	public boolean hasChanges(long id) throws ServiceExecutionException {
		Object trx = getTrx(id);
		id = getTrxId (id);
		if (trx == null) {
			throw new ServiceExecutionException("No Transaction Found [" + id
					+ "] to be released");
		}
		UnitOfWork uow = ((UnitOfWork) trx);
		return uow.hasChanges();
	}

	public void setDebug(boolean _debug) {
		debug = _debug;
	}

	public boolean getDebug() {
		return debug;
	}

	public void console(String str) {
		if (debug)
			System.out.println("\t[PERSISTENCE-CONSOLE]--> " + str);
	}

	private void removeTrx(long id) {
		Long lId = new Long(id);
		trxs.remove(lId);
		trxObjects.remove(lId);
		writeProviderCode("trxs.remove(new Long(" + id + "));");
	}

	private void writeProviderCode(String str) {
		topLinkCodeTrad.append(str).append("\n\r");
	}

	public String getProviderCode() {
		return "==================== TOPLINK CODE ====================\r\n"
				+ topLinkCodeTrad.toString()
				+ "======================================================";
	}

	private class TrxObjects {
		long trxId = NULL_TRANSACTION_ID;

		Hashtable objects = new Hashtable ();

		TrxObjects(long _trxId) {
			trxId = _trxId;
		}

		long getTrxId() {
			return trxId;
		}

		void registerObject(Object object) {
			if (object != null)
				objects.put(new Integer(object.hashCode()), object);
		}

		void registerAllObjects(Collection _objects) {
			if (_objects != null && !_objects.isEmpty()) {
				Iterator iterObjects = _objects.iterator();
				while (iterObjects.hasNext()) {
					Object object = iterObjects.next();
					objects.put(new Integer(object.hashCode()), object);
				}
			}
		}

		Object getObject(int hashCode) {
			return objects.get(new Integer(hashCode));
		}

		boolean containsObject(int hashCode) {
			return objects.containsKey(new Integer(hashCode));
		}

		void removeObject(int hashCode) {
			objects.remove(new Integer(hashCode));
		}
	}

	private void verifySession() throws ServiceExecutionException {
		if (clientSession == null) {
			throw new ServiceExecutionException (
					"No Database Session created for this instance of DAO ["
							+ this.hashCode() + "]");
		}
	}

}